﻿#region copyright
/*
 * Author: Jan Vilimek: jan (at) vilimek (dot) cz
 * If you use any code from this program/library, please include above line
 * 
 */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace pdb2mobiGUI
{
	/// <summary>
	/// PDB file reader...
	/// PDB format see http://wiki.mobileread.com/wiki/PDB#Palm_Database_File_Code
	/// PalmDOC format see http://wiki.mobileread.com/wiki/PalmDOC
	/// </summary>
	class PDBFile
	{
		public readonly PDBHeader Header = new PDBHeader();
		public readonly List<PDBRecord> Records = new List<PDBRecord>();

		private bool DataRead = false;
		public Exception Error;
		public readonly StringBuilder Log = new StringBuilder();
		public string originalFileName { get; private set; }

		public PDBFile()
		{
			//Header.Reset();
		}

		public bool Read(string fileName, bool readData)
		{
			Header.Reset();
			Records.Clear();
			DataRead = false;
			Error = null;
			originalFileName = fileName;
			try
			{
				using (FileStream fs = new FileStream(fileName, FileMode.Open))
				using (BinaryReader br = new BinaryReader(fs))
				{
					if (!this.Header.Read(br))
						throw new InvalidOperationException("invalid header!");
					if (this.Header.NumRecords <= 0)
						throw new InvalidOperationException("No records in file!");
					if (this.Header.DbType != "TEXt")
						throw new InvalidOperationException(string.Format("DB Type is not 'TEXt' but [{0}]", this.Header.DbType));
					PDBRecord prevRecord = null;
					for (int i = 0; i < this.Header.NumRecords; i++)
					{
						var record = new PDBRecord();
						if (!record.Read(br))
							throw new InvalidOperationException("Invalid record in file!");
						if (prevRecord != null)
							prevRecord.dataLength = (int)(record.offset - prevRecord.offset);
						prevRecord = record;
						Records.Add(record);
					}
					if (readData)
					{
						///in case we need to read content of the book we would evaluate through records. However this is not needed ATM
						ReadDataInternal(br);
					}
				}
			}
			catch (Exception ex)
			{
				this.Error = ex;
				return false;
			}
			return true;
		}

		public bool ReadDataInternal(BinaryReader br)
		{
			foreach (var record in Records)
			{
				if (br.BaseStream.Position > record.offset)
					return false;
				if (br.BaseStream.Position < record.offset)
					br.BaseStream.Seek(record.offset, SeekOrigin.Begin);
				if (record.dataLength.HasValue)
					record.Data = br.ReadBytes(record.dataLength.Value);
				else
					record.Data = br.ReadBytes((int)(br.BaseStream.Length - br.BaseStream.Position));
			}
			DataRead = true;
			return true;
		}
	}
}
